# Author : zheng.cuizh@gmail.com
# RoadRunner named lik LoadRunner which is a industry performance test tool of HP.
# I'm happy to user LR and Ruby,
# so i coded the RoadRunner.
# Using RR,you'll find somethings similar to LR.

$:.unshift File.dirname(__FILE__)
require 'rubygems'
require 'benchmark'
require 'logger'
require 'pp'

Dir[File.join(File.dirname(__FILE__),"*.rb")].select{|x|not (x =~ /.*model\.rb/ || x =~ /.*db\.rb/ || x =~ /.*roadrunner\.rb/)}.each { |x| require x  }

class RoadRunner

  include RoadRunnerModule
  attr :iterations, true
  attr :users,true
  attr :global,true
  attr :iterationId
  attr :record
  attr :userId
  attr :mode,true
  attr :log
  attr :tps
  alias_method :g,:global
  alias_method :g=,:global=

  def initialize(opts={})
    # => DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
    opts = {:out=>File.join(File.dirname(__FILE__),'..','log',"stdout.log"),:frequency=>'daily',:size=>1048576,:level=>Logger::DEBUG}.merge opts
    @users,@iterations,@longest,@userId,@iterationId=1,1,0,0,0
    @initBlk,@actBlk,@endBlk=proc{},proc{},proc{}
    @global,@transactions={},{}
    @log=Logger.new(opts[:out],opts[:frequency],opts[:size])
    @log.level=opts[:level]
    # => mode : sequence<default>,thread|t,process|p
    @mode='sequence'

    if block_given? then
      begin
        gem 'active_record'
        require 'active_record'
      rescue Exception => e
        require 'activerecord'
      end
      
      require 'db'
      require "model"

      session = {}      
      _session = yield session
      session = _session if session == {}

      begin
        ActiveRecord::Base.establish_connection(
#
#          === session defined in block :
#          
#          :adapter=>session[:adapter],
#          :encoding=>session[:encoding],
#          :database=>session[:database],
#          :username=>session[:username],
#          :password=>session[:password],
#          :host=>session[:host]
#
          session
        )
        self.log.info "connect db ok."
      rescue =>ex
        self.log.error "adapter:#{session[:adapter]}.connect db faile."
      end

      default_report_table = %w"scenarios transactions records"

      unless @data_write = default_report_table.inject(true){|r,t| r = r && ActiveRecord::Base.connection.tables.include?(t)} then
        self.log.warn "table rreports doesn't defined and will be created."
        Dbhelper::migrate default_report_table
        if @data_write = default_report_table.inject(true){|r,t| r = r && ActiveRecord::Base.connection.tables.include?(t)} then
          self.log.info "create table #{default_report_table.inspect} successful."
        else
          self.log.error "create table #{default_report_table.inspect} fail."
        end
      end
      require 'model' unless defined? Scenario
      self.log.debug 'model Rreport is reqruired.'
    end
  end

  def transaction(name,&blk)
    @transactions[name] || @transactions[name] = []
    status = "AUTO"
    # => Status is the last expression in the action block
    rcost = Benchmark::realtime{status = yield} if block_given?
    # => status's value is:
    # => 0=>success
    # => -1=>faile
    case status
    when false,'false','-1',-1 then status = -1
    else status = 0
    end
    # {:stats=>status,:cost=>rcost,:create_at=>Time.now} is one record
    @transactions[name] << {:stats=>status,:cost=>rcost,:create_at=>Time.now}
    # => the below sentence cost a lot of system resource
    # => if you run for production result,keep it annotated!!!
#    self.log.debug @transactions[name].inspect
    rcost
  end

  def method_missing(name,*args,&blk)
    self.transaction(name.to_s,&blk)
  end
    
end
